#ifndef PW_PBL_IFORWARD_HPP
#define PW_PBL_IFORWARD_HPP

#include "Interface.hpp"

namespace pw_pbl::Interface
{
	namespace _fwd
	{
		struct TWrappedConstructor;

		struct SVAssignment
		{
			using TConstructor = void(const void *, void *);

			size_t size;
			size_t align;
			TConstructor *CopyTo;
			void (*MoveTo)(void *, void *);

			struct SConstructTo
			{

				TConstructor *pFunc;
				const void *pObj;

				explicit inline operator bool() noexcept { return pFunc != nullptr; }
				inline void operator()(void *pTgt) noexcept { return pFunc(pObj, pTgt); }
			};

			template <bool isRvalue>
			inline SConstructTo GetConstructor(const void *pobj) const noexcept { return {.pFunc = isRvalue && MoveTo ? (TConstructor *)MoveTo : CopyTo, .pObj = pobj}; }
			inline SConstructTo GetConstructor(const void *pobj, bool isRvalue) const noexcept { return {.pFunc = isRvalue && MoveTo ? (TConstructor *)MoveTo : CopyTo, .pObj = pobj}; }

			constexpr std::tuple<size_t, size_t, TWrappedConstructor> WrappedConstructor(const void *pobj, bool isRvalue, const _imp::TVObject<> &vobj) const noexcept;
		};

		template <typename T>
		static constexpr SVAssignment cVAssignment = {
			.size = sizeof(T),
			.align = alignof(T),
			.CopyTo = std::is_copy_constructible_v<T> ? [](const void *This, void *p) { new (p) T{*(const T *)This}; } : nullptr,
			.MoveTo = std::is_move_constructible_v<T> ? [](void *This, void *p) { new (p) T{std::move(*(T *)This)}; } : nullptr,
		};

		struct TWrappedConstructor
		{
			SVAssignment::SConstructTo RawConstructor;
			_imp::TVObject<> VObject;

			inline void operator()(void *p) noexcept
			{
				auto *ptr = (_imp::TVObject<> *)p;
				*ptr = VObject;
				RawConstructor(ptr->pObj());
			}
			explicit inline operator bool() noexcept { return static_cast<bool>(RawConstructor); }
		};

		constexpr std::tuple<size_t, size_t, TWrappedConstructor> SVAssignment::WrappedConstructor(const void *pobj, bool isRvalue, const _imp::TVObject<> &vobj) const noexcept
		{
			TWrappedConstructor out;
			out.RawConstructor = GetConstructor(pobj, isRvalue);
			if (out.RawConstructor)
			{
				out.VObject = vobj;
				size_t size, align;
				// 以下动态计算未知类型TWrapped的大小和对齐
				constexpr auto EAlignMask = alignof(_imp::TVObject<>) - 1;
				if (this->align <= alignof(_imp::TVObject<>))
					size = this->size + sizeof(_imp::TVObject<>) + EAlignMask & ~EAlignMask, align = alignof(_imp::TVObject<>);
				else
					size = this->size + this->align, align = this->align;
				return {size, align, out};
			}
			return {0, 0, out};
		}

		struct empty
		{
			constexpr empty(auto &&...) noexcept {}
		};

	} // namespace _fwd

	/// 接口的万能转发器，可从左值和右值引用构造
	/// @tparam DSC 接口的描述
	/// @tparam ...LRVALUE 左右值限定选项，为空表示左右值引用均可，为true表示右值，为false表示左值
	template <typename DSC, bool... RVALUE>
	class TForwarder
	{
		static constexpr bool EIsUniversal = sizeof...(RVALUE) == 0;
		static constexpr bool EIsRvalue = (RVALUE && ...);
		template <typename T>
		static constexpr bool ECanFromRvalue = (EIsUniversal || EIsRvalue) && (std::is_move_constructible_v<T> || std::is_copy_constructible_v<T>);
		template <typename T>
		static constexpr bool ECanFromLvalue = (EIsUniversal || !EIsRvalue) && std::is_copy_constructible_v<T>;

		const _imp::SVTable<DSC> *_mpVTable;
		void *_mpObj;
		const _fwd::SVAssignment *pVAssignment;
		[[no_unique_address]] std::conditional_t<EIsUniversal, bool, _fwd::empty> isRvalue;

	public:
		template <_imp::ccImp<DSC, TForwarder> T> requires ECanFromLvalue<T>
		inline constexpr TForwarder(T &obj) noexcept : _mpVTable{&AutoVTable<DSC, T>}, _mpObj{&obj}, pVAssignment{&_fwd::cVAssignment<T>}, isRvalue{false}
		{}

		template <_imp::ccImp<DSC, TForwarder> T> requires(!ECanFromLvalue<T>)
		TForwarder(T &) = delete;

		template <_imp::ccImp<DSC, TForwarder> T> requires ECanFromRvalue<T>
		inline constexpr TForwarder(T &&obj) noexcept : _mpVTable{&AutoVTable<DSC, T>}, _mpObj{&obj}, pVAssignment{&_fwd::cVAssignment<T>}, isRvalue{true}
		{}

		template <_imp::ccImp<DSC, TForwarder> T> requires(!ECanFromRvalue<T>)
		TForwarder(T &&) = delete;

		explicit inline TForwarder(const _imp::SVTable<DSC> &vtable, void *pObj, const _fwd::SVAssignment &vassi, decltype(isRvalue) rv = {}) noexcept : TRef<DSC>{vtable, pObj}, pVAssignment{&vassi}, isRvalue{rv} {}
		TForwarder(TForwarder &&) = default;
		TForwarder(const TForwarder &) = delete;
		template <bool... ORVALUE> requires EIsUniversal || (EIsRvalue == TForwarder<DSC, ORVALUE...>::EIsRvalue)
		TForwarder(TForwarder<DSC, ORVALUE...> oth) noexcept : TRef<DSC>{oth}, pVAssignment{oth.pVAssignment}
		{
			if constexpr (oth.EIsUniversal)
				isRvalue = oth.isRvalue;
			else if constexpr (oth.EIsRvalue)
				isRvalue = true;
			else
				isRvalue = false;
		}

		template <typename T>
		TForwarder(TRef<T>) = delete;
		template <typename T, bool... OV>
		TForwarder(TForwarder<T, OV...>) = delete;

		inline constexpr operator TRef<DSC>() const noexcept { return *TPtr<DSC>{*_mpVTable, _mpObj}; }

		inline constexpr size_t Size() noexcept { return pVAssignment->size; }
		inline constexpr size_t Align() noexcept { return pVAssignment->align; }
		inline constexpr void *pObj() noexcept { return this->_mpObj; }
		inline const _imp::SVTable<DSC> &VTable() const noexcept { return *this->_mpVTable; }
		inline constexpr _fwd::SVAssignment::SConstructTo Constructor() noexcept
		{
			if constexpr (EIsUniversal)
				return pVAssignment->GetConstructor(this->pObj(), isRvalue);
			else
				return pVAssignment->GetConstructor<EIsRvalue>(this->pObj());
		}

		constexpr std::tuple<size_t, size_t, _fwd::TWrappedConstructor> WrappedConstructor() noexcept
		{
			if constexpr (EIsUniversal)
				return pVAssignment->WrappedConstructor(this->pObj(), isRvalue, _imp::TVObject<DSC>{pVAssignment->align > alignof(_imp::TVObject<DSC>), this->VTable()});
			else
				return pVAssignment->WrappedConstructor(this->pObj(), EIsRvalue, _imp::TVObject<DSC>{pVAssignment->align > alignof(_imp::TVObject<DSC>), this->VTable()});
		}
	};

} // namespace pw_pbl::Interface

namespace pw_pbl
{
	/// 可转移对象接口
	class IForward : public Interface::TBox<IForward>
	{
		using _tBox = Interface::TBox<IForward>;
		using _tBox::_tBox;

	public:
		struct TRawVTable
		{
			const Interface::_fwd::SVAssignment *pVAssignment;
		};

		struct TRawRef
		{
			LForwarder GetLForwarder(this const auto &self) noexcept { return LForwarder{self.VTable(), self.pObj(), self.VTable().TRawVTable::pVAssignment}; }
			RForwarder GetRForwarder(this const auto &self) noexcept { return RForwarder{self.VTable(), self.pObj(), self.VTable().TRawVTable::pVAssignment}; }
		};

		template <typename _tObj>
		static constexpr bool CanFrom = (std::is_copy_constructible_v<_tObj> || std::is_move_constructible_v<_tObj>);

		template <typename _tObj>
		static constexpr std::tuple<> ClassVTable{};
	};
	template <typename _tObj> requires IForward::CanFrom<_tObj>
	constexpr IForward::TRawVTable IForward::ClassVTable<_tObj>{&Interface::_fwd::template cVAssignment<_tObj>};
} // namespace pw_pbl

#endif // PW_PBL_IFORWARD_HPP
